package com.chunayev.numerology.util.db;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;


import com.chunayev.numerology.domain.BaseEntity;
import com.chunayev.numerology.domain.Chakra;
import com.chunayev.numerology.domain.Plan;
import com.chunayev.numerology.domain.PositionSummary;
import com.chunayev.numerology.util.Constants;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.SequenceConfig;
import com.sleepycat.persist.EntityCursor;
import com.sleepycat.persist.EntityStore;
import com.sleepycat.persist.PrimaryIndex;
import com.sleepycat.persist.StoreConfig;
import com.sleepycat.persist.evolve.IncompatibleClassException;

/**
 * @author Sergey Chunayev
 */
public class DBInitializer {
    
    public static final EntityStore CHAKRA_ENTITY_SOTRE;
    public static final PrimaryIndex<Long, Chakra> CHAKRA_PRIMARY_INDEX;
    public static final EntityStore PLAN_ENTITY_STORE;
    public static final PrimaryIndex<Long, Plan> PLAN_PRIMARY_INDEX;
    public static final EntityStore SUMMARY_ENTITY_STORE;
    public static final PrimaryIndex<Long, PositionSummary> SUMMARY_PRIMARY_INDEX;
    private static final Environment ENV;
    
    static {
        try {
            final boolean isInit = new Boolean(Constants.APPLICATION_PROPERTIES
                    .getProperty("clear.data"));
            final String envFile = Constants.APPLICATION_PROPERTIES
                    .getProperty(Constants.ENV_HOME);
            final File dir = new File(envFile);
            final boolean init = isInit || !dir.exists();
            if (init) {
                initDir(dir);
            }
            
            final EnvironmentConfig environmentConfig = new EnvironmentConfig();
            environmentConfig.setAllowCreate(true);
            environmentConfig.setSharedCache(true);
            ENV = new Environment(new File(Constants.APPLICATION_PROPERTIES
                    .getProperty(Constants.ENV_HOME)), environmentConfig);
            
            SUMMARY_ENTITY_STORE = initEntityStore(PositionSummary.class, 1L);
            SUMMARY_PRIMARY_INDEX = SUMMARY_ENTITY_STORE.getPrimaryIndex(
                    Long.class, PositionSummary.class);
            PLAN_ENTITY_STORE = initEntityStore(Plan.class);
            PLAN_PRIMARY_INDEX = PLAN_ENTITY_STORE.getPrimaryIndex(Long.class,
                    Plan.class);
            CHAKRA_ENTITY_SOTRE = initEntityStore(Chakra.class);
            CHAKRA_PRIMARY_INDEX = CHAKRA_ENTITY_SOTRE.getPrimaryIndex(
                    Long.class, Chakra.class);
            if (init) {
                initChakras();
                initPlans();
                initSummary();
            }
            
        } catch (final Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    public static void close() throws DatabaseException {
        CHAKRA_ENTITY_SOTRE.close();
        PLAN_ENTITY_STORE.close();
        SUMMARY_ENTITY_STORE.close();
        ENV.close();
    }
    
    private static void initChakras() throws DatabaseException {
        initEntities(CHAKRA_ENTITY_SOTRE, "chakras", Chakra.class);
    }
    
    /**
     * @throws IOException
     * @throws FileNotFoundException
     * @throws DatabaseException
     */
    private static void initDir(final File dir) throws FileNotFoundException,
            IOException, DatabaseException {
        if (dir.exists()) {
            final File[] files = dir.listFiles();
            for (final File file : files) {
                file.delete();
            }
            dir.delete();
        }
        
        dir.mkdir();
        
    }
    
    private static <T extends BaseEntity> void initEntities(
            final EntityStore entityStore, final String propertyKey,
            final Class<T> entityClass) throws DatabaseException {
        final String propertyValue = Constants.APPLICATION_PROPERTIES
                .getProperty(propertyKey);
        final String[] tokens = propertyValue.split(",");
        final PrimaryIndex<Long, T> pi = entityStore.getPrimaryIndex(
                Long.class, entityClass);
        try {
            for (int i = 0; i < tokens.length; i++) {
                final T entity = entityClass.newInstance();
                entity.setName(tokens[i]);
                pi.put(entity);
            }
        } catch (final IllegalAccessException e) {
            throw new DatabaseException(e);
        } catch (final InstantiationException e) {
            throw new DatabaseException(e);
        }
    }
    
    private static EntityStore initEntityStore(final Class<?> entityClass)
            throws IncompatibleClassException, DatabaseException {
        return initEntityStore(entityClass, 0L);
    }
    
    private static EntityStore initEntityStore(final Class<?> entityClass,
            final long startIndex) throws IncompatibleClassException,
            DatabaseException {
        final StoreConfig storeConfig = new StoreConfig();
        storeConfig.setAllowCreate(true);
        final EntityStore entityStore = new EntityStore(ENV, entityClass
                .getSimpleName(), storeConfig);
        final SequenceConfig sequenceConfig = new SequenceConfig();
        sequenceConfig.setAllowCreate(true);
        sequenceConfig.setInitialValue(startIndex);
        entityStore.setSequenceConfig(String.format("%sSequence", entityClass
                .getSimpleName()), sequenceConfig);
        return entityStore;
    }
    
    private static void initPlans() throws DatabaseException {
        initEntities(PLAN_ENTITY_STORE, "plans", Plan.class);
    }
    
    private static void initSummary() throws DatabaseException {
        final List<Chakra> chakras = retrieveAll(CHAKRA_PRIMARY_INDEX);
        final List<Plan> plans = retrieveAll(PLAN_PRIMARY_INDEX);
        
        for (final Plan p : plans) {
            for (final Chakra c : chakras) {
                final PositionSummary summary = new PositionSummary();
                summary.setChakraId(c.getId());
                summary.setPlanId(p.getId());
                SUMMARY_PRIMARY_INDEX.put(summary);
                
            }
        }
        
    }
    
    private static <E> List<E> retrieveAll(final PrimaryIndex<Long, E> pi)
            throws DatabaseException {
        final List<E> result = new ArrayList<E>();
        final EntityCursor<E> entities = pi.entities();
        for (final E e : entities) {
            result.add(e);
        }
        entities.close();
        
        return result;
    }
}
